package com.github.framework.webmvc.servlet;


import com.github.framework.annotaion.*;
import com.github.framework.context.ApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @program: springv1
 * @description: 职责：请求分发，调度
 * @author: Pop
 * @create: 2021-06-14 22:12
 **/
public class PopDispatchServlet extends HttpServlet {

    // 较仿造spring的容器的初始化
    private ApplicationContext context;

    // url 与 method映射关系
    private List<HandlerMapping> handlerMappings = new ArrayList<HandlerMapping>();

    // handlerMapping 与 handlerAdapter的关联关系
    private Map<HandlerMapping,HandlerAdapter> handlerAdapters =  new HashMap<HandlerMapping,HandlerAdapter>();


    private List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 委派，当执行时，会使用url和真正的method方法进行关联
        try{
            doDispatch(req,resp);
        }catch (Exception e)
        {
            e.printStackTrace();
            try {
                processDispatchResult(req,resp,new ModelAndView("500"));
            }catch (Exception e1)
            {
                e1.printStackTrace();
            }

        }
    }


    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        // 通过一个url获得一个HandlerMapping
        HandlerMapping handlerMapping = getHandler(req);

        if(Objects.isNull(handlerMapping))
        {
            processDispatchResult(req,resp,new ModelAndView("404"));
            return;
        }

        // 然后通过HandlerMapping 获得一个 HandlerAdapter
        HandlerAdapter adapter = getHandlerAdapter(handlerMapping);
        // 可以适配出一个modelAndView
        ModelAndView modelAndView = adapter.handler(req,resp,handlerMapping);
        // 将一个modelAndView变成一个ViewResolver
        processDispatchResult(req,resp,modelAndView);
    }

    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
        if(handlerAdapters.isEmpty()){return null;}
        return handlerAdapters.get(handlerMapping);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView mv)  throws  Exception{
        if(mv==null){return ;}
        if(viewResolvers.isEmpty()){return;}
        for(ViewResolver vr:viewResolvers)
        {
            View view = vr.resolveViewName(mv.getViewName());
            // 开始渲染工作，往浏览器写
            view.render(mv.getModel(),req,resp);
            return;
        }
    }
    /**
     * 获得handlerMapping
     * @param req
     * @return
     */
    private HandlerMapping getHandler(HttpServletRequest req) {
        if(handlerMappings.isEmpty()){return null;};
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");

        for(HandlerMapping handlerMapping : handlerMappings)
        {
            Matcher matcher = handlerMapping.getUrl().matcher(url);
            if(!matcher.matches()){continue;}
            return handlerMapping;
        }
        return null;
    }


    @Override
    public void init(ServletConfig config) throws ServletException {

        // v2版本，模拟spring容器的初始化
        context = new ApplicationContext(config.getInitParameter("contextConfigLocation"));
        // 初始化的重头戏
        initStrategies(context);
        System.out.println("Spring 容器初始化完成");

    }

    /**
     *
     * 初始化九大组件 mvc
     * @param context
     */
    private void initStrategies(ApplicationContext context) {
//        //多文件上传的组件
//        initMultipartResolver(context);
//        //初始化本地语言环境
//        initLocaleResolver(context);
//        //初始化模板处理器
//        initThemeResolver(context);
        //handlerMapping
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);
//        //初始化异常拦截器
//        initHandlerExceptionResolvers(context);
//        //初始化视图预处理器
//        initRequestToViewNameTranslator(context);
        //初始化视图转换器
        initViewResolvers(context);
//        //FlashMap管理器
//        initFlashMapManager(context);
    }

    private void initViewResolvers(ApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templatePath");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootFile = new File(templateRootPath);
        //将这个目录下的所有文件都转换成ViewResolver
        for(File file : templateRootFile.listFiles())
        {
            //这里扎那是不考虑文件夹的问题
            viewResolvers.add(new ViewResolver(templateRoot));
        }
    }

    private void initHandlerAdapters(ApplicationContext context) {
        for (HandlerMapping handler : handlerMappings)
        {
            handlerAdapters.put(handler,new HandlerAdapter());
        }
    }

    private void initHandlerMappings(ApplicationContext context) {
        if(context.getBeanDefinitionCount()==0){return;}
        for (String beaName : context.getBeanDefinitionNames()) {
            Object instance = context.getBean(beaName);
            Class<?> clazz = instance.getClass();
            if(!clazz.isAnnotationPresent(Controller.class))
            {
                continue;
            }
            String baseUrl = "";
            if(clazz.isAnnotationPresent(RequestMapping.class))
            {
                baseUrl+=clazz.getAnnotation(RequestMapping.class).value();
            }
            // 这里只获取公共的方法，spring也是这样做的
            Method[] methods = clazz.getMethods();
            for(Method method:methods)
            {
                if(!method.isAnnotationPresent(RequestMapping.class))
                {
                    continue;
                }
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                // 由于 我们的url可能会写成/ add*.json 这里将内容转换成java中正则的内容
                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
//                handlerMapping.put(url,method);
                handlerMappings.add(new HandlerMapping(Pattern.compile(regex),method,instance));
                System.out.println("Mapped : "+regex+" + "+method);
            }
        }
    }

}
